import { reactive } from "vue";
import { forEachValue } from './utils';
import { storeKey } from './injectKey';
export default class Store {
  constructor(options) {
    const store = this;
    // 保存 state,reactive包一下就是响应式了,加个data是为了方便赋值
    store._state = reactive({ data: options.state });
    // 保存 getter
    store.getters = {};
    const _getters = options.getters;
    // 处理getter,把每一个方法结果存在getters上
    forEachValue(_getters, (fn, key) => {
      Object.defineProperty(store.getters, key, {
        // getter第一个参数是state
        // vuex4的getter无法用计算属性,因此不会被缓存,因为组件销毁时会将会将计算属性一起销毁,从而失去这个getter
        // vuex3.2版本还没解决
        get: () => fn(store.state),
        // 可以枚举
        enumerable:true
      });
    })
    // 保存 mutations的方法集合
    store._mutations = Object.create(null);
    const _mutations = options.mutations;
    forEachValue(_mutations, (mutation, key) => {
      store._mutations[key] = (payload) => {
        // mutations this指向store实例,第一个参数state,第二个是传进来的参数(只能是一个,不支持多参数)
        mutation.call(store, store.state, payload);
      };
    });
    // 保存 actions的方法集合
    store._actions = Object.create(null);
    const _actions = options.actions;
    forEachValue(_actions, (action, key) => {
      store._actions[key] = (payload) => {
        // action this指向store实例,第一个参数state,第二个是传进来的参数(只能是一个,不支持多参数)
        action.call(store, store, payload);
      };
    });
  }
  // 触发mutations的方法,type:类型,payload:参数
  // 要用箭头函数稳住this指向,不然解构的话就乱了
  commit = (type, payload) => {
    // 去mutations里找到并把参数传进去执行
    this._mutations[type](payload);
  }
  // 提交actions的方法,type:类型,payload:参数
  // 要用箭头函数稳住this指向,不然解构的话就乱了
  dispatch = (type, payload) => {
    // 去actions里找到并把参数传进去执行
    this._actions[type](payload);
  }
  // 拦截get,返回真正的state
  get state() {
    return this._state.data;
  }
  // vue.use会自动调用此方法,app(是vue实例),injectKey是store的名字可以不传
  install(app, injectKey = storeKey) {
    // 把这个store实例注入到app中,暴露成全局变量,第一个参数类似key,第二个类型value
    app.provide(injectKey, this);
    // 为了$store能用,把store暴露到app上,类似以前 Vue.prototype.$store = this;
    app.config.globalProperties.$store = this;
  }
}